/*
 * Copyright 2002-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ruoyi.common.filter;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.Constants;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.AbstractRequestLoggingFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.function.Predicate;

/**
 * Simple request logging filter that writes the request URI
 * (and optionally the query string) to the Commons Log.
 *
 * @author Rob Harrop
 * @author Juergen Hoeller
 * @see #setIncludeQueryString
 * @see #setBeforeMessagePrefix
 * @see #setBeforeMessageSuffix
 * @see #setAfterMessagePrefix
 * @see #setAfterMessageSuffix
 * @see org.apache.commons.logging.Log#debug(Object)
 * @since 1.2.5
 */
public class RequestLoggingFilter extends AbstractRequestLoggingFilter implements Ordered {

    private String messagePrefix = DEFAULT_AFTER_MESSAGE_PREFIX;

    private String messageSuffix = DEFAULT_AFTER_MESSAGE_SUFFIX;

    private boolean beforeLog = false;

    @Override
    public void setAfterMessagePrefix(String afterMessagePrefix) {
        super.setAfterMessagePrefix(afterMessagePrefix);
        this.messagePrefix = afterMessagePrefix;
    }

    @Override
    public void setAfterMessageSuffix(String afterMessageSuffix) {
        super.setAfterMessageSuffix(afterMessageSuffix);
        this.messageSuffix = afterMessageSuffix;
    }

    @Override
    protected boolean shouldLog(HttpServletRequest request) {
        return logger.isInfoEnabled();
    }

    public void setBeforeLog(boolean beforeLog) {
        this.beforeLog = beforeLog;
    }

    /**
     * Writes a log message before the request is processed.
     */
    @Override
    protected void beforeRequest(HttpServletRequest request, String message) {
        request.setAttribute(Constants.START_TIME, System.currentTimeMillis());
        if (beforeLog) {
            logger.info(message);
        }
    }

    /**
     * Writes a log message after the request is processed.
     */
    @Override
    protected void afterRequest(HttpServletRequest request, String message) {
        Long startTime = (Long) request.getAttribute(Constants.START_TIME);
        long executeTime = System.currentTimeMillis() - startTime;

        logger.info("请求开始时间：" + DateUtil.date(startTime) + ", 执行时间：" + executeTime + "ms, " + StrUtil.sub(message, 0, 1000));
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        boolean isFirstRequest = !isAsyncDispatch(request);
        HttpServletRequest requestToUse = request;

        if (isIncludePayload() && isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) {
            requestToUse = new ContentCachingRequestWrapper(request, getMaxPayloadLength());
        }

        boolean shouldLog = shouldLog(requestToUse);
        if (shouldLog && isFirstRequest) {
            beforeRequest(requestToUse, beforeLog ? createMessage(requestToUse, this.messagePrefix, this.messageSuffix) : "");
        }
        try {
            filterChain.doFilter(requestToUse, response);
        } finally {
            if (shouldLog && !isAsyncStarted(requestToUse)) {
                afterRequest(requestToUse, createMessage(requestToUse, response, this.messagePrefix, this.messageSuffix));
            }
        }
    }

    protected String createMessage(HttpServletRequest request, HttpServletResponse response, String prefix, String suffix) {
        StringBuilder msg = new StringBuilder();
        msg.append(prefix);
        msg.append(request.getMethod()).append(' ');
        msg.append(request.getRequestURI());

        if (isIncludeQueryString()) {
            String queryString = request.getQueryString();
            if (queryString != null) {
                msg.append('?').append(queryString);
            }
        }

        msg.append(", status=").append(response.getStatus());

        if (isIncludeClientInfo()) {
            String client = request.getRemoteAddr();
            if (StringUtils.hasLength(client)) {
                msg.append(", client=").append(client);
            }
            HttpSession session = request.getSession(false);
            if (session != null) {
                msg.append(", session=").append(session.getId());
            }
            String user = request.getRemoteUser();
            if (user != null) {
                msg.append(", user=").append(user);
            }
        }

        if (isIncludeHeaders()) {
            HashMap<String, String> headers = new HashMap<>();
            Predicate<String> headerPredicate = getHeaderPredicate();
            Enumeration<String> names = request.getHeaderNames();
            while (names.hasMoreElements()) {
                String header = names.nextElement();
                if (headerPredicate != null) {
                    if (headerPredicate.test(header)) {
                        headers.put(header, request.getHeader(header));
                    }
                } else {
                    headers.put(header, request.getHeader(header));
                }
            }
            msg.append(", headers=").append(JSON.toJSONString(headers));
        }

        if (isIncludePayload()) {
            // 使用的是可缓存流,并且如果接口没有读取body,这里不会有值
            String payload = getMessagePayload(request);
            if (payload == null) {
                payload = "{}";
            }
            msg.append(", payload=").append(payload.replace(System.lineSeparator(), ""));
        }

        msg.append(suffix);
        return msg.toString();
    }


    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 10;
    }
}
